#ifndef __CGE_PROPERTY_H__
#define __CGE_PROPERTY_H__

#include "cgeCommons.h"

#define PROP_READONLY	0
#define PROP_WRITEONLY	1
#define PROP_READWRITE	2

#define prop(h, v, a, m) cge::ess::Property<h, v, a, m>
#define acce(h, v, a, m) cge::ess::Property<h, v, a, m>::Accessor

//! Usage : prop[s|i][ro|wo|rw]

#define propsro(h, v) \
	friend acce(h, v, void, PROP_READONLY); \
	prop(h, v, void, PROP_READONLY)

#define propswo(h, v) \
	friend acce(h, v, void, PROP_WRITEONLY); \
	prop(h, v, void, PROP_WRITEONLY)

#define propsrw(h, v) \
	friend acce(h, v, void, PROP_READWRITE); \
	prop(h, v, void, PROP_READWRITE)

#define propiro(h, v, a) \
	friend acce(h, v, a, PROP_READONLY); \
	prop(h, v, a, PROP_READONLY)

#define propiwo(h, v, a) \
	friend acce(h, v, a, PROP_WRITEONLY); \
	prop(h, v, a, PROP_WRITEONLY)

#define propirw(h, v, a) \
	friend acce(h, v, a, PROP_READWRITE); \
	prop(h, v, a, PROP_READWRITE)

namespace cge
{
	namespace std
	{
		template<class THost, class TVal, class TArg, u8 Mode>
		class Property;

		template<class THost, class TVal>
		class Property<THost, TVal, void, PROP_READONLY>
		{
			friend THost;

		public:
			typedef TVal (THost::*GetMethod)() const;

			class Accessor
			{
				friend Property;
			
			public:
				operator TVal() const
				{
					return (mHost->*mGetMethod)();
				}

			private:
				THost* mHost;
				GetMethod mGetMethod;
			};

		public:
			operator TVal() const
			{
				return (TVal)mAcc;
			}

		private:
			Property(THost* host, GetMethod getMethod)
			{
				mAcc.mHost = host;
				mAcc.mGetMethod = getMethod;
			}

		private:
			Accessor mAcc;
		};

		template<class THost, class TVal>
		class Property<THost, TVal, void, PROP_WRITEONLY>
		{
			friend THost;

		public:
			typedef void (THost::*SetMethod)(const TVal&);

			class Accessor
			{
				friend Property;
			
			public:
				void operator = (const TVal& val)
				{
					(mHost->*mSetMethod)(val);
				}

			private:
				THost* mHost;
				SetMethod mSetMethod;
			};

		public:
			void operator = (const TVal& val)
			{
				mAcc = val;
			}

		private:
			Property(THost* host, SetMethod setMethod)
			{
				mAcc.mHost = host;
				mAcc.mSetMethod = setMethod;
			}

		private:
			Accessor mAcc;
		};

		template<class THost, class TVal>
		class Property<THost, TVal, void, PROP_READWRITE>
		{
			friend THost;

		public:
			typedef TVal (THost::*GetMethod)() const;
			typedef void (THost::*SetMethod)(const TVal&);

			class Accessor
			{
				friend Property;
			
			public:
				operator TVal() const
				{
					return (mHost->*mGetMethod)();
				}

				void operator = (const TVal& val)
				{
					(mHost->*mSetMethod)(val);
				}

			private:
				THost* mHost;
				GetMethod mGetMethod;
				SetMethod mSetMethod;
			};

		public:
			operator TVal() const
			{
				return (TVal)mAcc;
			}

			void operator = (const TVal& val)
			{
				mAcc = val;
			}

		private:
			Property(THost* host, GetMethod getMethod, SetMethod setMethod)
			{
				mAcc.mHost = host;
				mAcc.mGetMethod = getMethod;
				mAcc.mSetMethod = setMethod;
			}

		private:
			Accessor mAcc;
		};

		template<class THost, class TVal, class TArg>
		class Property<THost, TVal, TArg, PROP_READONLY>
		{
			friend THost;

		public:
			typedef TVal (THost::*GetMethod)(const TArg&) const;

			class Accessor
			{
				friend Property;
			
			public:
				operator TVal() const
				{
					return (mHost->*mGetMethod)(*mIndex);
				}

			private:
				THost* mHost;
				const TArg* mIndex;
				GetMethod mGetMethod;
			};

		public:
			Accessor operator [](const TArg& idx) const
			{
				mAcc.mIndex = &idx;
				return mAcc;
			}

		private:
			Property(THost* host, GetMethod getMethod)
			{
				mAcc.mHost = host;
				mAcc.mGetMethod = getMethod;
			}

		private:
			mutable Accessor mAcc;
		};

		template<class THost, class TVal, class TArg>
		class Property<THost, TVal, TArg, PROP_WRITEONLY>
		{
			friend THost;

		public:
			typedef void (THost::*SetMethod)(const TArg&, const TVal&);

			class Accessor
			{
				friend Property;
			
			public:
				void operator = (const TVal& val)
				{
					(mHost->*mSetMethod)(*mIndex, val);
				}

			private:
				THost* mHost;
				const TArg* mIndex;
				SetMethod mSetMethod;
			};

		public:
			Accessor operator [](const TArg& idx)
			{
				mAcc.mIndex = &idx;
				return mAcc;
			}

		private:
			Property(THost* host, SetMethod setMethod)
			{
				mAcc.mHost = host;
				mAcc.mSetMethod = setMethod;
			}

		private:
			Accessor mAcc;
		};

		template<class THost, class TVal, class TArg>
		class Property<THost, TVal, TArg, PROP_READWRITE>
		{
			friend THost;

		public:
			typedef TVal (THost::*GetMethod)(const TArg&) const;
			typedef void (THost::*SetMethod)(const TArg&, const TVal&);

			class Accessor
			{
				friend Property;
			
			public:
				operator TVal() const
				{
					return (mHost->*mGetMethod)(*mIndex);
				}

				void operator = (const TVal& val)
				{
					(mHost->*mSetMethod)(*mIndex, val);
				}

			private:
				THost* mHost;
				const TArg* mIndex;
				GetMethod mGetMethod;
				SetMethod mSetMethod;
			};

		public:
			const Accessor operator [](const TArg& idx) const
			{
				mAcc.mIndex = &idx;
				return mAcc;
			}

			Accessor operator [](const TArg& idx)
			{
				mAcc.mIndex = &idx;
				return mAcc;
			}

		private:
			Property(THost* host, GetMethod getMethod, SetMethod setMethod)
			{
				mAcc.mHost = host;
				mAcc.mGetMethod = getMethod;
				mAcc.mSetMethod = setMethod;
			}

		private:
			mutable Accessor mAcc;
		};
	}
}

#endif//__CGE_PROPERTY_H__